class ExtGrapesjs {
  constructor() {
    this.componentAll = {};
  }

  init(param = {}) {
    this.name = this.constructor.name;
    console.log(this.name + " init...");

    const { container, scripts, styles } = param;

    if (!container) {
      throw this.name + " init param error,container is Empty";
    }

    //--- 初始化编辑器
    this.editor = grapesjs.init({
      container: container,
      height: "auto",
      width: "auto",
      fromElement: true,
      //不显示项目默认面板
      panels: { defaults: [] },
      canvas: {
        //加载js文件
        scripts: scripts || [],
        //加载css文件
        styles: styles || [],
      },
      storageManager: {
        type: "local", // 存储在本地
        autosave: true, // 自动存储
        autoload: false, //自动加载（在初始化界面的时候加载）
        stepsBeforeSave: 1, // 在修改了多少次时自动加载
      },
      blockManager: {
        // appendTo: "#blocks",
      },
    });

    //----- 初始化包装器（显示面板）

    const wrapper = this.getWrapper();
    wrapper.set({
      removable: false, //组件是否移除
      draggable: false, //是否可以拖到其他地方
      droppable: false, //是否可以添加其他组件
      badgable: false, //是否显示标签名字
      stylable: false, //是否可以设置样式
      copyable: false, //是否可以复制
      resizable: false, //是否可以调整大小
      editable: false, //是否可以编辑
      // layerable: false, //是否在图层中隐藏
      selectable: false, //是否允许选择组件
      hoverable: false, //鼠标悬停在元素上时显示突出显示轮廓
    });
    wrapper.addClass("eg-wrapper");

    //初始化主面板

    this.addComponentType({
      type: "eg-mainPanel",
      domText: "<div></div>",
      attr: {
        removable: false, //组件是否移除
        draggable: false, //是否可以拖到其他地方
        droppable: true, //是否可以添加其他组件
        badgable: false, //是否显示标签名字
        stylable: false, //是否可以设置样式
        copyable: false, //是否可以复制
        resizable: false, //是否可以调整大小
        editable: false, //是否可以编辑
        // layerable: false, //是否在图层中隐藏
        // selectable: false, //是否允许选择组件
        hoverable: false, //鼠标悬停在元素上时显示突出显示轮廓
      },
    });
    var mainPanel = wrapper.append({ type: "eg-mainPanel" })[0];
    mainPanel.addClass("eg-mainPanel");
  }

  /**
   *
   *
   */
  addComponentType(param = {}) {
    const { type, domText, attr = {}, extPropChange = {} } = param;
    if (!type) {
      throw "createComponent type is Empty";
    }

    //解析domText
    var dom = this.parseDom(domText);
    var domProps = this.parseDomProps(dom);

    /**
     * 如果defaults里面有style，在init()里面修改Attributes的参数会报错
     * 解决方法：将标签属性里面的style转换为普通css样式
     */
    var propStyle = domProps.style;
    var propStyleClassName = "eg-propStyle-" + type;
    delete domProps.style;

    this.editor.DomComponents.addType(type, {
      //绑定标签名字，标签名字需要大写
      isComponent: (el) => el.tagName === type.toUpperCase(),
      model: {
        defaults: {
          tagName: dom.tagName,
          components: dom.innerHTML,
          attributes: {
            ...domProps,
            class: propStyle ? propStyleClassName : "",
          },
          ...attr,
          extProp: {},
          styles: propStyle
            ? "." + propStyleClassName + "{" + propStyle + "}"
            : "",
        },
        init() {
          const extComponent = new ExtComponent(this);
          this.extComponent = extComponent;

          // this.set({ ...attr });
          //禁止添加其他组件
          // this.set({ droppable: false });
          // this.components().forEach((c) => {
          //   //禁用组件及其子项的选择
          //   c.set({ locked: true });
          // });
          //kkkk
          // this.on("change:attributes:kkk", function (component, value) {
          //   console.log("***********************");
          //   console.log("handleChange kkk = " + value);
          //   console.log(component);
          //   // component.addStyle({ color: value });
          // });
          // this.addAttributes({ kkk: 111111111 });
        },
      },
    });
    //---
    this.componentAll[type] = {
      type,
      domText,
    };
  }

  addBlock(param = {}) {
    const { id, label, labelEl, type, changeView } = param;
    const blockId = id || type + "-" + this.genAutoSerial();
    this.editor.BlockManager.add(blockId, {
      label: label,
      labelEl: labelEl,
      content: { type: type },
      changeView: changeView,
    });
  }

  /**
   * 初始化包装器（显示面板）
   */
  getWrapper() {
    return this.editor.Components.getWrapper();
  }

  /**
   * 操作面板
   */
  getMainPanel() {
    return this.getWrapper().components().at(0);
  }

  /**
   * 被选择的组件
   */
  getSelected() {
    return this.editor.getSelected();
  }

  genProjectData() {
    var mainPanel = this.getMainPanel();
    const data = {
      type: mainPanel.get("type"),
      items: [],
    };
    mainPanel.components().forEach((c) => {
      data.items.push({
        type: c.get("type"),
      });
    });

    return data;
  }

  setProjectData(data) {
    var mainPanel = this.getMainPanel();
    mainPanel.empty();
    if (data) {
      data.items.forEach((d) => {
        mainPanel.append({ type: d.type });
      });
    }
  }

  parseDom(htmlText) {
    var objE = document.createElement("div");
    //去除头尾空格，防止空格成为节点
    htmlText = htmlText == null ? "" : htmlText.replace(/(^\s*)|(\s*$)/g, "");
    objE.innerHTML = htmlText;
    return objE.childNodes[0];
  }

  parseDomProps(dom) {
    var domPropsMap = dom.attributes;
    var domPropsObj = {};
    for (var i in domPropsMap) {
      var domPropObj = domPropsMap[i];
      domPropObj.nodeName &&
        (domPropsObj[domPropObj.nodeName] = domPropObj.nodeValue);
    }
    return domPropsObj;
  }

  /**
   * 生成新的序号
   */
  genAutoSerial() {
    return (this.autoSerial = (this.autoSerial || 0) + 1);
  }
}

/**
 *
 */
class ExtComponent {
  constructor(component) {
    this.component = component;
    component.extComponent = this;
  }

  // setProp(name,value){
  // }
}

class OutGrapesjs {
  /**
   *
   * @param para.container container element or element selector
   */
  init(param = {}) {
    var itself = this;
    this.name = this.constructor.name;
    console.log(this.name + " init...");

    this.initFinishEvents = [];

    const { container, onInitFinish } = param;

    if (!container) {
      throw this.name + " init param error,container is Empty";
    }

    var containerEl =
      typeof container == "string"
        ? document.querySelector(container)
        : container;
    if (!containerEl) {
      throw this.name + " init param error,container element no existent";
    }

    containerEl.innerHTML = `
      <style type="">
      .og-frame {
        outline: medium none;
        height: 100%;
        width: 100%;
        border: none;
        margin: auto;
        display: block;
        transition: width 0.35s ease, height 0.35s ease;
        position: absolute;
        top: 0;
        bottom: 0;
        left: 0;
        right: 0;
      }
    </style>
    <iframe id="og-iFrame" src="./index.html" class="og-frame">
      <p>加载界面失败</p>
    </iframe>
      `;

    this.iFrame = document.getElementById("og-iFrame");
    this.frameAction = new FrameAction(window, this.iFrame.contentWindow);

    this.iFrame.onload = function () {
      if (onInitFinish) {
        itself.frameAction.postAsync("onInitFinish", null, function () {
          onInitFinish();
        });
      }
    };

    //---- init end
  }

  genProjectData() {
    return this.frameAction.post("genProjectData");
  }

  setProjectData(data) {
    this.frameAction.post("setProjectData", data);
  }

  addInitFinishEvent(eFun) {
    this.initFinishEvents.push(eFun);
    if (this.iFrameInitFinish) {
      eFun();
    }
  }
}

class FrameAction {
  constructor(currWindow, targetWindow) {
    this.POST_TYPE_REQ = "request";
    this.POST_TYPE_RESP = "response";

    this.currWindow = currWindow;
    this.targetWindow = targetWindow;
    // id to function
    this.postWaitFuns = {};
    // name to function
    this.postEventFuns = {};

    var itself = this;
    this.currWindow.addEventListener(
      "message",
      function (e) {
        var info = e.data;
        var postType = info?.postType;

        //响应请求数据
        if (postType == itself.POST_TYPE_REQ) {
          var postEventFun = itself.postEventFuns[info.name];
          if (postEventFun) {
            postEventFun(info);
          } else {
            //默认不返回数据
            itself.getDefaultPostEvent()(info);
          }
        }

        //监听返回数据
        if (postType == itself.POST_TYPE_RESP) {
          var postWaitFun = itself.postWaitFuns[info.id];
          postWaitFun && postWaitFun(info);
        }
      },
      false
    );
  }

  post(name, data) {
    var itself = this;
    return new Promise((resolve, reject) => {
      itself.postAsync(name, data, function (resData) {
        resolve(resData);
      });
    });
  }

  postAsync(name, data, fun) {
    var itself = this;
    var info = {
      id: itself.genAutoSerial(),
      postType: this.POST_TYPE_REQ,
      name,
      data,
    };
    //监听数据返回
    itself.postWaitFuns[info.id] = function (resInfo) {
      delete itself.postWaitFuns[info.id];
      fun(resInfo.data);
    };
    //发送数据
    itself.targetWindow.postMessage(info, "*");
  }

  setPostEvent(name, fun) {
    var itself = this;
    itself.postEventFuns[name] = function (reqInfo) {
      var respData = fun(reqInfo.data);
      //发送数据
      itself.targetWindow.postMessage(
        {
          id: reqInfo.id,
          postType: itself.POST_TYPE_RESP,
          name,
          data: respData,
        },
        "*"
      );
    };
  }

  setAsyncPostEvent(name, fun) {
    var itself = this;
    itself.postEventFuns[name] = function (reqInfo) {
      fun(reqInfo.data, function (respData) {
        //发送数据
        itself.targetWindow.postMessage(
          {
            id: reqInfo.id,
            postType: itself.POST_TYPE_RESP,
            name,
            data: respData,
          },
          "*"
        );
      });
    };
  }

  getDefaultPostEvent() {
    var defaultName = "default_post_type_fun";
    var postEventFun = this.postEventFuns[defaultName];
    if (!postEventFun) {
      this.setPostEvent(defaultName, function () {});
    }
    return this.postEventFuns[defaultName];
  }

  /**
   * 生成新的序号
   */
  genAutoSerial() {
    return (this.currWindow.autoSerial = (this.currWindow.autoSerial || 0) + 1);
  }
}
